home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 2002 November / SGI Freeware 2002 November - Disc 1.iso / dist / fw_exmh.idb / usr / freeware / lib / exmh-2.5 / ispell.tcl.z / ispell.tcl
Text File  |  2002-07-08  |  30KB  |  918 lines

  1. #################################################################################
  2. # TCL Interactive Spell Checker Version 1.01 
  3. # Developed for use in EXMH by John McLaughlin (johnmcl@sr.hp.com) 6/7/97
  4. # This new spell checking code for EXMH was developed out of
  5. # frustration with the current spell checking EXMH code
  6. # mostly I could not get it to work the way I wanted it to.
  7. # Because I couldn't get the spell checker to work as well
  8. # as I would like, I found myself spending inordinate amounts
  9. # of time when writing e-mail, constantly fretting over
  10. # the spelling.  This little piece of code is the result
  11. # and seems to work pretty well in my environment.  I have
  12. # tested this under both linux (redhat 4.0) HPUX 9.05
  13. # This software operates in one of two different
  14. # modes, it can either spell check as you type, marking words
  15. # in a variety of ways (underline, bold, italic, etc) that are
  16. # not spelled correctly.  Also it can put a button in the sedit
  17. # window to allow in place spell checking. (The button option
  18. # requires changes to your .exmh-defaults file)
  19. #
  20. # This code depends on the excellent 'ispell' program and most
  21. # of the variables & procedure's get their name from it.
  22. # This code was developed under ispell v3.1.20
  23. #
  24. #######################################################################
  25. #             INSTALLATION
  26. # to Use: 3 easy steps (Note: EXMH should not be running when you do this)
  27. #
  28. # Note 2: This may be incorporated into a 'core' EXMH release in 
  29. # the future so below may not be required.....
  30. #
  31. # 1) add to the file ~/.tk/exmh/user.tcl in the function 'User_Init'
  32. #    a call to ispell_Preferences.  If you don't have a user.tcl
  33. #    get one, Usually it can be found in '/usr/local/exmh-<version>'
  34. #    where <version> is the version of exmh you are running
  35. #    if you are really desperate for a user.tcl the following
  36. #    should work (just make a file called user.tcl with the
  37. #    following line.....
  38. #    proc User_Init {} { ispell_Preferences }
  39. #
  40. # 2) add the following to your .exmh-defaults at the TOP of the file
  41. # *Sedit.Menubar.ubuttonlist: ispell
  42. # *Sedit.Menubar.ispell.text: Ispell        
  43. # *Sedit.Menubar.ispell.command: Ispell_CheckEntireWindow $t
  44. #
  45. # 3) in your ~/.tk/exmh directory type 'wish' then type 
  46. #         auto_mkindex . *.tcl
  47. #    then type 'exit'
  48. #  
  49. # That should be it!  There should be a preferences menu for 'I-Spell' now to allow
  50. # control of various parts of the ispell package... Also the 'Sedit' window should
  51. # have a 'ispell' button to check the entire document...
  52. # if a word is marked misspelled right click on it to add to dictionary or
  53. # select an alternate version
  54. #
  55. ############## Trouble Shooting ######################
  56. #
  57. # Did you make sure that....
  58. # Ispell was turned on? (from preferences/I-spell  menu)
  59. # A 'Miss-Spelled word style' is selected? (from preferences/I-spell)
  60. # the User Library directory is ~/.tk/exmh (preferences/Hacking Support)
  61. # the changes above were made with EXMH NOT running?
  62. #
  63. # You are using the correct version of ispell?  it works against ispell
  64. # version 3.0 & 3.1, version 4.0 (which seems to be a sort of 'non product'
  65. # is reported NOT to work.  most un*x users seem to have 3.1 installed..
  66. #
  67. ############## Performance ##########################
  68. #
  69. # This spell checker seems to operate without any 
  70. # obvious performance drag when typing in sedit
  71. # with enough ram, most modern workstations should 
  72. # be able to use this without any obvious performance hit
  73. # I find running it under Linux with a 40mb P90
  74. # quite comfortable
  75. # as a few benchmarks, this software can spell check
  76. # a 2700 word letter in 12 seconds on a P90 running
  77. # linux (redhat 4.0) with 40mb of ram.  other 
  78. # timing results indicate a user can expect 
  79. # a 1.3ms to 3.0ms additional delay for
  80. # correctly spelled words, and up to 30ms
  81. # for words not spelled correctly.  As always
  82. # ram helps, ram starved machines will 
  83. # not fair as well
  84. ######################################################
  85. #
  86. # Enjoy, if you find it useful or have any comments
  87. # please let me know (johnmcl@sr.hp.com) also if you 
  88. # make any improvements please send them to me
  89. #
  90. # John McLaughlin, HP Santa Rosa, January 1997 (johnmcl@sr.hp.com)
  91. ######################################################
  92.  
  93. #########################################################
  94. # ispell_init is called to start the entire process off
  95. #########################################################
  96. proc Ispell_Init { } { 
  97.     global ispellVars
  98.  
  99.     if {! [ info exists ispellVars(currentLanguage) ] } {
  100.     set ispellVars(currentLanguage) ""
  101.     }
  102.  
  103.     if { $ispellVars(currentLanguage) == $ispellVars(language) } return ; 
  104.  
  105.     set ispellVars(currentLanguage) $ispellVars(language)  ; # mark current language
  106.  
  107.  
  108.     # These things are now specified by the 'Preferences' menu
  109.     #    a good choice f0or the spell command is "ispell -a"
  110.     #   
  111.     set ispellVars(last_word) "dummy" 
  112.     # this variable are the alternate spellings of the misspelled word...
  113.     set ispellVars(choices) "" 
  114.  
  115.     # how to view, see the text.n man page for other ideas
  116.     # options include -background <color> -foreground <color> 
  117.     # -font <font> etc..
  118.     #    set ispellVars(viewStyle) "-underline t"
  119.  
  120.     if { [ info exists ispellVars(spell_buffer) ] } {
  121.     if { [catch {
  122.         close $ispellVars(spell_buffer)
  123.     } reason] } {
  124.          Exmh_Debug Ispell Buffer Closed:$reason
  125.     }
  126.     }
  127.  
  128.     if {!$ispellVars(on)} {
  129.     set ispellVars(currentLanguage) "disabled"
  130.     }
  131.     if {$ispellVars(currentLanguage) == "disabled"} {
  132.     catch {unset ispellVars(spell_buffer) }
  133.     return ;
  134.     }
  135.  
  136.     if {[info exists ispellVars(command)]} {
  137.     set cmd $ispellVars(command)
  138.     } else {
  139.     set cmd $ispellVars(defaultCommand)
  140.     }
  141.     if [catch {open "|$cmd" r+} ispellVars(spell_buffer)] {
  142.     set ispellVars(on) 0    ;# triggers trace
  143.     return
  144.     }
  145.  
  146.     # Poke the process because:
  147.     # 4.0 doesn't respond with a full line, so gets hangs
  148.     # 2.0 doesn't output a version number.
  149.     # We only like 3.*
  150.  
  151.     puts $ispellVars(spell_buffer) "?"
  152.     flush $ispellVars(spell_buffer)
  153.     gets $ispellVars(spell_buffer) line
  154.     set version {"unknown"}
  155.     if {([string compare $line *] == 0) ||
  156.     ([regexp {[Vv]ersion ([0-9])\.} $line x version] &&
  157.         $version != 3)} {
  158.     # Oh No!  Why are you not running version 3.X???
  159.     # specifically version 4.0 doesn't work!!!
  160.     Exmh_Status "Ignoring ispell version $version (3.* required)"
  161.     catch { close $ispellVars(spell_buffer) } reason
  162.     set ispellVars(on) 0
  163.     return
  164.     }
  165.     #
  166.     # Since this is the right version, we need to read the (blank) reply to
  167.     # the "?" we sent...
  168.     #
  169.     gets $ispellVars(spell_buffer) line
  170.  
  171.     IspellWriteSpellBuffer "!" ; # enter terse mode
  172. }
  173.  
  174. ######################
  175. # preferences
  176. ######################
  177.  
  178. proc Ispell_Preferences {} { 
  179.  
  180.     Preferences_Add "I-Spell" \
  181.         "This is a module to allow interactive spelling within a sedit window
  182. it has many fine features include suggested correction and the ability
  183. to add new words to a session or to your personnel dictionary.
  184. For words that are either not correct or not generated 
  185. by a combination of roots or compounds, the word is marked as not
  186. spelled correctly." { 
  187.  
  188.     { ispellVars(on) ispellOnOff ON {Turn Ispell On/Off} 
  189.     "This turns the ispell feature on/off.  Note that the feature
  190.     needs to be enabled BEFORE a message is brought up" } 
  191.  
  192.     { ispellVars(ReCheckAfterAdd) ispellRecheckAfterAdd ON {Re-Verify after Adds?} 
  193.     "Check this box if you want to re spell check words 
  194.     currently marked Miss Spelled after you add to the dictionary 
  195.     or session.  In general a good idea except if you work 
  196.     in extremely long documents a small delay may be noticed 
  197.     after you add words to your personal dictionary
  198.     Additionally the right mouse button can be used to
  199.     accept suggested words" }
  200.  
  201.     { ispellVars(textOnly) ispellTextOnly OFF {Spell Check 'Text' only?} 
  202.     "Check this box if you want to only perform spell checking 
  203. of the text marked as 'text', this should avoid spell checking 
  204. To:, CC: & X-Face: lines, it comes at a small time penalty, turn it off
  205. if you want to see a small improvement in response time
  206. Note that turning this option on will also not spell check 'attachements'
  207. unless they are marked as Content-Type: text/enriched or text/plain.
  208. if you find that spell checking STOPS working in a section of the document you
  209. may want to turn this off" }
  210.  
  211.     { ispellVars(defaultCommand) ispellCommand "ispell -a -S" {Default speller invocation}
  212.     "This is the program used to actually do the real work
  213. 'ispell -a' is probably a good choice.  if you want to 
  214. use an alternate dictionary, 'ispell -a -d <dictionary-file>' may be 
  215. appropriate. you may find that -S sorts the list of possible
  216. words better, see the ispell(1) man page for more details.
  217. (really, it's got a lot of details and you can really personalize
  218. how it works to fit best with your environment" } 
  219.     { ispellVars(otherCommands) ispellOthers " German  { ispell -a -d deutsch } \
  220.         French { ispell -a -d francais } English { ispell -a -d english } " { Other Invocations }
  221.     "Alternate Invocations of of the 'Ispell' programs, mostly intended for 
  222. our friends in Europe who have to work in a variety of languages, this entry should
  223. be in label/invocation pairs" } 
  224.  
  225.     
  226.     { ispellVars(popupBinding) ispellPopupBinding "ButtonPress-3" {Menu popup bound to:}
  227.     " This controls what the 'popup' window is bound to, some examples include:
  228. ButtonPress-3 
  229. ButtonPress-2
  230. ButtonPress-1
  231. Shift-3
  232. Control-3
  233. Meta-2
  234. Alt-1
  235. etc...
  236.  
  237. Note that the menu is unposted on any ButtonRelease" }
  238.  
  239.     { ispellVars(viewStyle) ispellStyle {CHOICE underline italic bold bgcolor fgcolor other } {Miss-spelled word style}
  240.     " this is how to display misspelled words
  241.     use the built in types or create your own
  242.     using 'other', for 'color' ones fill in the color 
  243.     examples using other include 
  244.     -underline t
  245.     -background red
  246.     -foreground Bisque
  247.     -font <font>
  248.     -fgstipple <bitmap>
  249.     -bgstipple <bitmap>
  250.  
  251.     Bitmap's can be many things, 'gray50' and 'gray25' are popular
  252.  
  253.     For example....
  254.  
  255.     -font *italic*
  256.     or   -font *bold*
  257.     or   -font *24*    (Big!)
  258.  
  259.     or   -font *italic*24* (big italics)
  260.  
  261.     -relief <relief> (see tk doc's for more info...)
  262.  
  263.     
  264.     Effects can also be combined as in 
  265.     
  266.     -underline t -foreground red
  267.     -bgstipple gray25 -color red
  268.  
  269.     
  270.     " } 
  271.     { ispellVars(viewStyle-Color) ispellStyleColor red {color:} 
  272.     "color for fgcolor and bgcolor" }
  273.     { ispellVars(viewStyle-Other) ispellStyleOther {-underline t -foreground red}  {other:}
  274.     "Style if 'other' is selected" }
  275.     }
  276.     if { [ info exists ispellVars(CheckButton) ] } {
  277.     if {$ispellVars(CheckButton) == 1} {
  278.  
  279.         option add *Sedit.Menubar.ubuttonlist {ispell}
  280.         
  281.         option add *Sedit.Menubar.ispell.text {Ispell}
  282.         
  283.         option add *Sedit.Menubar.ispell.command {Ispell_CheckEntireWindow $t}
  284.     }
  285.     }
  286.     global ispellVars
  287.     set ispellVars(language) default
  288.     trace variable ispellVars(on) w IspellOnOff    
  289. }
  290.  
  291. proc IspellOnOff {args} {
  292.     global ispellVars
  293.     catch {unset ispellVars(currentLanguage)}
  294.     Ispell_Init
  295. }
  296.  
  297.  
  298.  
  299. # a safe procedure to write to the ispell buffer
  300. # this procedure dumps the variable 'word' to the spell buffer
  301. # if the buffer has died, it will restart it
  302.  
  303. proc IspellWriteSpellBuffer { word } {
  304.     global ispellVars
  305.     
  306.     if {$ispellVars(currentLanguage) == "disabled"} { return * } ;
  307.  
  308.     puts $ispellVars(spell_buffer) $word
  309.     if { [ catch { flush $ispellVars(spell_buffer) } ] } {
  310.     Exmh_Debug "Ispell process terminated!!!!!, temp disabling"
  311.     set ispellVars(language) disabled
  312.     Ispell_Init
  313.     return "*" ; # return if we had to restart
  314.     }
  315. }
  316.  
  317. # This procedure kills the ispell buffer
  318. proc Ispell_Kill {} { 
  319.     global ispellVars
  320.     close $ispellVars(spell_buffer)
  321.     set ispellVars(on) 0
  322. }
  323.  
  324. ##########################################
  325. # this is the proc that does the 
  326. # actual spell checking, it will return a 
  327. # '*' if everything is cool, otherwise
  328. # it returns a list of possible miss-spelled
  329. # words.  See ispell(1) for more details
  330. proc IspellWords line { 
  331.     global ispellVars
  332.  
  333.     regsub -all { +} $line { } line        ;# compress out extra spaces
  334.     set count [llength [split $line { }]]    ;# Count space separated words
  335.     set result ""
  336.  
  337.     if { $ispellVars(currentLanguage) == "disabled" } { return "*" } ;
  338.  
  339.     # clear out the fileevent
  340.     if { [ catch {fileevent $ispellVars(spell_buffer) readable {} } ] } {
  341.     Ispell_Init
  342.     return "*"
  343.     }
  344.     # so the puts stuff doesn't freak out
  345.     # CRITCAL prepend a '^' to keep the buffer from freaking
  346.     puts $ispellVars(spell_buffer) "^$line"
  347.     # we have to put the ^ in front of the line so ispell works correctly
  348.     # see ispell(1) for more details
  349.     if { [ catch { flush $ispellVars(spell_buffer) } ] } {
  350.     Exmh_Debug "Ispell process terminated!!!!!, restarting"
  351.     Ispell_Init
  352.     return "*" ; # return if we had to restart
  353.     }
  354.  
  355.     # loop through list of words, usually there is just 1
  356.     for { set i 0 } { $i <= $count } {  incr i } { 
  357.     gets $ispellVars(spell_buffer) var
  358.     if {$var == {} } then {
  359.         lappend result "*";
  360.         break;
  361.     }
  362.     lappend result $var
  363.     }
  364.     # invoke a fileevent to help flush out the data so wer are always in sync
  365.     fileevent $ispellVars(spell_buffer) readable {
  366.     global ispellVars
  367.     gets $ispellVars(spell_buffer) dummy 
  368.     }
  369.     return $result
  370. }
  371.  
  372.  
  373. # this proc spell checks the word under the current cursor
  374. # marking it with a 'MissSpelled' tag if it is in fact incorrect
  375. # text is the text window
  376. # This version runs about 300us slower than the previous
  377. # version using tk's built in 'wordstart' and 'wordend'
  378. # (1.3ms vs 1.7ms)
  379.  
  380. proc IspellTextWindow { text } { 
  381.  
  382. #################
  383. # WARNING
  384. # CHANGES FOR SUPPORT OF EUROPEAN CHARACTERS!!
  385. ##########################
  386.     set start [ $text get "insert linestart" insert ]
  387.     
  388.     set end   [ $text get insert "insert lineend" ] 
  389.     
  390.     set e1 ""
  391.     set s1 ""
  392.     
  393.     regexp "\[^\t \]*" $end e1
  394.     set e1 [ string trim $e1 ] 
  395.     regexp "\[^\t \]+$" $start s1
  396.     
  397.     set startIndex "insert - [string length $s1] chars"
  398.     set stopIndex "insert + [string length $e1] chars "
  399.  
  400.     set word "[ string trim $s1$e1 "\"\{\}\[\] \t" ] "
  401.  
  402.     IspellMarkWord $text $startIndex $stopIndex $word 
  403. }
  404.  
  405. # this Proc is to spell check words that with 'inserts'
  406. # i.e. after 'space', 'tab' etc.... This version
  407. # runs at exactly the same speed as the tk built
  408. # in version (1.3ms) so I feel pretty comfortable
  409. # that this shouldn't effect speed too much
  410. # all test times were gotten via 'time' 
  411. # and thus may have some errors (especially 
  412. # with regexps, I think the system compiles
  413. # them).  In this version I can't use
  414. # tk's built in 'word' functions because
  415. # they don't allow for european characters....
  416.  
  417. proc IspellTextWindowInsert { text } { 
  418. #################
  419. # WARNING
  420. # CHANGES FOR SUPPORT OF EUROPEAN CHARACTERS!!
  421. ##########################
  422.     set start [ $text get "insert linestart" insert ]
  423.     
  424.     set s1 ""
  425.     
  426.     # now let's pick off the last word
  427.     regexp "\[^\t ]+$" $start s1
  428.  
  429.  
  430.     set startIndex "insert - [string length $s1] chars"
  431.     set stopIndex "insert" 
  432.  
  433.     set word " [ string trim $s1 "\"\{\}\[\]  \t" ] "
  434.  
  435.     IspellMarkWord $text $startIndex $stopIndex $word 
  436.  
  437. }
  438.  
  439. ####################################################
  440. # proc to mark words in the text window, with the given 
  441. # indexes, the 'word' is the word in question
  442. ####################################################
  443. proc IspellMarkWord {text startIndex stopIndex word} {
  444.  
  445.     global ispellVars ;
  446.  
  447.     #first let's not mark the word bad if we aren't in a section marked 'type=text*"
  448.  
  449.     if {$ispellVars(textOnly)} {
  450.     if { ! [ string match "*type=text*" [ $text tag names insert ] ] } { return * } ;
  451.     }
  452.  
  453.     set result [ IspellWords $word ];
  454.     #    * means fine, + means a root?, - means compount controlled by -C  option of ispell
  455.     if { ! [regexp {^[*+-]} $result ] } {
  456.     $text tag add MissSpelled $startIndex $stopIndex
  457.     set prompt "Suggested for $word: [ lreplace [ lindex $result 0 ] 0 3 ]"
  458.     # EXMH Specific 
  459.     SeditMsg $text $prompt
  460.     
  461.     } else {
  462.     $text tag remove MissSpelled $startIndex "$stopIndex +1c"
  463.     }
  464.     
  465.     set ispellVars(last_word) $word ; # store word so we don't re-check next
  466.     # time
  467.     return $result
  468. }
  469.  
  470.  
  471. ##############################################################
  472. # Proedure to call to mark words after the dictionary has been
  473. # modified, called from within the 'add' menus.....
  474. ##############################################################
  475.  
  476. proc IspellReCheckBuffer { window startIndex stopIndex word } { 
  477.     global ispellVars;
  478.     
  479.     # first let's make sure it's a real word....
  480.     if { $word == "" } return ;
  481.  
  482.     IspellMarkWord $window $startIndex $stopIndex $word; 
  483.  
  484.     # check word requested
  485.     if { [ info exists ispellVars(ReCheckAfterAdd) ] }  {
  486.     
  487.     if { $ispellVars(ReCheckAfterAdd) } { 
  488.         IspellReCheckWords $window ; 
  489.         # re-check buffer if requested..
  490.     }   
  491.     }
  492. }
  493.  
  494. ##########################################################
  495. # This proc will take the word currently under the mouse pointer
  496. # spell check it, and pop up a menu with suggestions or allowing
  497. # additions to the ispell-dictionary
  498. # 'text' is the text window, x,y are the co-ordinates relative to the
  499. # window, X,Y are the co-ordinates relative to the root window
  500. ##########################################################
  501.  
  502. proc IspellPostMenuChoices { text x y   X Y } { 
  503.  
  504.     global ispellVars;
  505.  
  506.     set adjustment {} 
  507.     set oldInsert [ $text index insert ] 
  508.     $text mark set insert "@$x,$y"
  509.  
  510.     set start [ $text get "insert linestart" insert ]
  511.     
  512.     set end   [ $text get insert "insert lineend" ] 
  513.     
  514.     set e1 ""
  515.     set s1 ""
  516.     
  517.     regexp "\[^\t \]*" $end e1
  518.     set e1 [ string trim $e1 ] 
  519.     regexp "\[^\t \]+$" $start s1
  520.  
  521.     set startIndex "insert - [string length $s1] chars"
  522.     set stopIndex "insert + [string length $e1] chars "
  523.     set word $s1$e1
  524.  
  525.     set word [ string trim $word  "\]\[\.\,\<\>\/\?\!\@\#\%\*0123456789\&\@\(\)\:\;\$ \{\}\"\\ \'\~\`\_\-\+\t\n\r\b\a\f\v\n "]   
  526.     set word [ string trim $word ]
  527.  
  528. #    set stopIndex [ $text index "@$x,$y wordend"  ]
  529. #    set startIndex [ $text index "$stopIndex  - 1 chars wordstart" ]
  530. #    set word  " [ string trim [ $text get $startIndex "$stopIndex wordend" ] \
  531. #        "\@\(\)\:\;\$ \{\}\"\\ \t\n\r\b\a\f\v\n "] " ; # "
  532. #    set word [ string trim $word ] ; # get rid of white space
  533.  
  534.     # if there is no word to mention, don't even post a menu...
  535.  
  536.     if { $word == "" } return ; 
  537.  
  538.     set result [ IspellMarkWord $text $startIndex $stopIndex $word ]
  539.  
  540.     $text mark set insert $oldInsert ; # get it back where it belongs
  541.     # create a meanu
  542.     set menu "$text.m"
  543.     catch { 
  544.     destroy $menu
  545.     }
  546.     menu $menu -tearoff f
  547.  
  548.     # remember the menu name so we can unpost it later.
  549.     set ispellVars(PopupMenu) $menu
  550.     
  551.     # first let's label the menu with the current language
  552.     $menu add command -label $ispellVars(language) -state disabled
  553.  
  554.     # now if spell checking is disabled, let's mark menus as such
  555.     set disFlag normal
  556.     if { $ispellVars(currentLanguage) == "disabled" } {
  557.     set disFlag "disabled" 
  558.     }
  559.  
  560.     $menu add separator 
  561.  
  562.     $menu add command -label "Add '$word' to Dictionary" -command  \
  563.         "IspellWriteSpellBuffer \"*$word\";\
  564.         IspellWriteSpellBuffer \#;\
  565.         IspellReCheckBuffer $text \"$startIndex\" \"$stopIndex\" $word;" -state $disFlag
  566.     # add word to dictionary, save dictionary, recheck word
  567.     
  568.     $menu add command -label "Accept '$word' for this session" -command \
  569.         "IspellWriteSpellBuffer \"@$word\";\
  570.         IspellReCheckBuffer $text \"$startIndex\"  \"$stopIndex\" $word;" -state $disFlag
  571.     # add word for this session, recheck word
  572.  
  573.     $menu add separator
  574.     foreach i   [ split [ lreplace [ lindex $result 0 ] 0 3 ] "," ]   {
  575.     set choice [ string trim $i ", " ]
  576.     $menu add command -label $choice -command "IspellReplaceWordInText $text $x $y \"$choice\" " 
  577.     }
  578.     $menu add separator
  579.  
  580.     menu $menu.sub -tearoff f
  581.  
  582.     $menu.sub add radiobutton -label "disabled" \
  583.         -command "set ispellVars(language) disabled ;
  584.     set ispellVars(command) \"\";
  585.     Ispell_Init" -variable ispellVars(language) -value "disabled"
  586.  
  587.  
  588.     $menu.sub add radiobutton -label "default" \
  589.     -command "set ispellVars(language) default ;
  590.         set ispellVars(command) \"$ispellVars(defaultCommand)\";
  591.         Ispell_Init" \
  592.     -variable ispellVars(language) -value "default"
  593.  
  594.     set count [ llength $ispellVars(otherCommands) ] 
  595.     for { set i 0 } { $i < $count } { incr i 2 } {
  596.     set lab  [ lindex $ispellVars(otherCommands) $i ]
  597.     set command  [ lindex $ispellVars(otherCommands) [ expr $i +1 ]  ]
  598.     $menu.sub add radiobutton -label "$lab " \
  599.         -command  " set ispellVars(language)  \"$lab\" ; 
  600.             set ispellVars(command) \"$command\"; 
  601.             Ispell_Init"  \
  602.         -variable ispellVars(language) -value "$lab"                       
  603.     }
  604.     
  605.     $menu add cascade -label "Alternate..." -menu $menu.sub
  606.  
  607.     tk_popup $menu $X $Y 
  608. }
  609.  
  610. #######################################################
  611. #
  612. # Procedure called to Unpost 
  613. # the menu
  614. #######################################################
  615. proc IspellUnPostMenuChoices {window } { 
  616.     global ispellVars
  617.  
  618.     catch {
  619.     tkMenuUnpost $ispellVars(PopupMenu)
  620.     }
  621. }
  622.  
  623. #########################################################
  624. # This proc will replace whatever word is listed at x,y
  625. # with 'word'  It goes to some lengths to keep surrouning
  626. # punctuation.
  627. #########################################################
  628. proc IspellReplaceWordInText { text x y word } { 
  629.  
  630.     set oldInsert [ $text index insert ] 
  631.  
  632.     $text mark set insert "@$x,$y"
  633.     
  634.     set start [ $text get "insert linestart" insert ]
  635.     
  636.     set end   [ $text get insert "insert lineend" ] 
  637.     
  638.     set e1 ""
  639.     set s1 ""
  640.     
  641.     regexp "\[^\t \]*" $end e1
  642.     set e1 [ string trim $e1 ] 
  643.     regexp "\[^\t \]+$" $start s1
  644.  
  645.     # If we are being asked to replace a word, first remove the tag
  646.     # so that whatever highlighting is there will be gone.
  647.     $text tag remove MissSpelled "insert - [string length $s1] chars" "insert + [string length $e1] chars "
  648.  
  649.     # Now let's clean up that string a bit..... remove punctuation & stuff
  650.  
  651.     set e1 [ string trim [ string trim $e1 ] "\]\[\.\,\<\>\/\?\!\@\#\%\*0123456789\&\@\(\)\:\;\$ \{\}\"\\ \'\~\`\_\-\+\t\n\r\b\a\f\v\n "]   
  652.  
  653.     set s1 [ string trim [ string trim $s1 ] "\]\[\.\,\<\>\/\?\!\@\#\%\*0123456789\&\@\(\)\:\;\$ \{\}\"\\ \'\~\`\_\-\+\t\n\r\b\a\f\v\n "]   
  654.  
  655.  
  656.     # now let's remove the old word & insert the new word.
  657.  
  658.     set startIndex "insert - [string length $s1] chars"
  659.     set stopIndex "insert + [string length $e1] chars "
  660.     set startInsert [ $text index $startIndex ] 
  661.  
  662.     $text delete $startIndex $stopIndex 
  663.     $text insert $startInsert $word
  664.     $text mark set insert $oldInsert ; # get it back where it belongs
  665. }
  666.  
  667. ##########################################################
  668. # EXMH Specific procedure to bind the window in question 
  669. # note that this has to be in the current process
  670. # it won't automagically be sucked in
  671. # a call to 'IspellPreferences' should do the trick...
  672. ##########################################################
  673. proc Hook_SeditInit_TagMissSpelled { file window } {
  674.     global ispellVars
  675.     # only configure the window for ispell support if it is
  676.     # actually needed, and if the appropriate variables exist
  677.     # 
  678.     # bind the window.....
  679.     # use default style of underline
  680.     set style "-underline t"
  681.  
  682.     if { [ catch {    
  683.     switch -exact -- $ispellVars(viewStyle) \
  684.         underline  { set style "-underline t"} \
  685.         italic     { set style "-font *italic*" } \
  686.         bold       { set style "-font *bold*"}  \
  687.         other      { set style "$ispellVars(viewStyle-Other)" } \
  688.         bgcolor    { set style "-background $ispellVars(viewStyle-Color)" } \
  689.         fgcolor    { set style "-foreground $ispellVars(viewStyle-Color)" } \ 
  690.     
  691.     
  692.     eval  $window tag configure MissSpelled $style
  693.     } result ] } {     
  694.     tk_dialog .window "Bad Style" \
  695.         "Invalid I-Spell style: '$result' changing to underline" \
  696.         {} 0 ok
  697.     eval $window tag configure MissSpelled -underline t
  698.     }
  699.     
  700.     # Only bind the window if 'ispell' is turned on...
  701.     if { [ info exists ispellVars(on) ] } {
  702.     if { $ispellVars(on) == 1 } { 
  703.         set ispellVars($window,effect) 1
  704.         Ispellbind $window 
  705.  
  706.     }
  707.     }
  708.  
  709.     set ispellVars(command) $ispellVars(defaultCommand)
  710.  
  711.     set ispellVars(language) "default" 
  712.  
  713.     # Only init the spell checker if it had already not been previously init'd
  714.     if { ! [ info exists ispellVars(spell_buffer) ] } { 
  715.     Ispell_Init ; # init if the spell buffer is undefine
  716.     }
  717.  
  718. # this procedure re-checks the entire buffer in the
  719. # window specified by 'window'
  720. proc Ispell_CheckEntireWindow { text } { 
  721.     global ispellVars
  722.  
  723.  
  724.     set oldInsert [ $text index insert ] 
  725.  
  726.     set count 0
  727.     # First things first, because this function COULD be called without
  728.     # using any of the other ispell stuff, first ensure that the ispell 
  729.     # process is running...
  730.     # Only init the spell checker if it had already not been previously init'd
  731.  
  732.     if { ! [ info exists ispellVars(spell_buffer) ] } { 
  733.     Ispell_Init ; # init if the spell buffer is undefine
  734.     }
  735.  
  736.     # Pop up a little window to allow spell checking to be turned off......
  737.     #
  738.     set ispellVars(label) "Stop Spell Checking"
  739.     catch { destroy .ispellStopWindow }
  740.     set top [ toplevel .ispellStopWindow ] 
  741.     wm group .ispellStopWindow .
  742.     button $top.b  -textvariable ispellVars(label) -command { 
  743.     set ispellVars(label) "" 
  744.     }
  745.     label $top.l1 -bitmap warning
  746.     label $top.l2 -bitmap warning
  747.     
  748.     pack $top.l1 -side left
  749.     pack $top.b -side left
  750.     pack $top.l2 -side left
  751.     
  752.     set endOfDoc [ $text index end ] ; # get the last index mark
  753.     set current 1.0
  754.  
  755.     # here is the actual code to spell check the document
  756.     while { [ expr $current < $endOfDoc ] }  {
  757.  
  758.     if { $ispellVars(label) == "" } { break}
  759.     $text mark set insert "$current"
  760.     set start [ $text get "insert linestart" insert ]
  761.     
  762.     set end   [ $text get insert "insert lineend" ] 
  763.     set e1 ""
  764.     set s1 ""
  765.     
  766.     regexp "\[^\t \]*" $end e1
  767. #    set e1 [ string trim $e1 ] 
  768.     regexp "\[^\t \]+$" $start s1
  769.     
  770.     set startIndex "insert - [string length $s1] chars"
  771.     set stopIndex "insert + [string length $e1] chars "
  772.     
  773.     set word "[ string trim $s1$e1 "\"\{\}\[\]" ] "
  774.     
  775.     set current [ $text index "$stopIndex + 2 chars" ]
  776.  
  777.     if {[string length $word] == 1} continue; # speed up process for small words....
  778.  
  779.     incr count
  780.     update
  781.  
  782.     if { $count > 120 } {
  783.         
  784.         $text see $current
  785.         set count 0
  786.     }
  787.  
  788.     IspellMarkWord $text $startIndex $stopIndex $word 
  789.     }
  790.     
  791.     destroy $top
  792.     # now let's redisplay the screen at the insert point....
  793.     $text mark set insert "$oldInsert"
  794.     $text see insert
  795. }
  796.  
  797.  
  798. ########################################################
  799. # procedure to re-check bound words to reconfirm they
  800. # are still missspelled
  801. # note that if quite a few words are missSpelled this could
  802. # take quite a while.... Also note that this should probably
  803. # only be called AFTER the dictionary has changed/updated
  804. ########################################################
  805.  
  806. proc IspellReCheckWords {window} { 
  807.     global ispellVars
  808.     
  809.  
  810.     set ranges [ $window tag ranges MissSpelled ] 
  811.     set ispellVars(label) "Stop Spell Checking"
  812.     set wcount 0
  813.  
  814.     if { [ expr [ llength $ranges ] > 100 ] } { 
  815.     # Only pop up a window if
  816.     # 100 or so need to be re-checked.
  817.     catch {
  818.         destroy .ispellStop
  819.     }
  820.  
  821.     toplevel .ispellStop
  822.     button .ispellStop.b  -textvariable ispellVars(label) -command { 
  823.         set ispellVars(label) "" 
  824.     }
  825.     label .ispellStop.l1 -bitmap warning
  826.     label .ispellStop.l2 -bitmap warning
  827.     
  828.     pack .ispellStop.l1 -side left
  829.     pack .ispellStop.b -side left
  830.     pack .ispellStop.l2 -side left
  831.     }
  832.  
  833.     # loop through all the current words marked as misspelled
  834.     #
  835.     for { set i 0 } { $i < [ expr [llength $ranges] / 2 ] } { incr i } {
  836.     set startIndex [ lindex $ranges [ expr $i*2 ] ] 
  837.     set stopIndex  [ lindex $ranges [ expr $i*2+1 ] ]
  838.     
  839.     if { [ $window compare "$startIndex + 1 chars" == "$stopIndex" ] } {
  840.         $window tag remove MissSpelled $startIndex "$stopIndex +1c"
  841.     }
  842.     set word  " [ string trim [ $window get $startIndex $stopIndex ] \
  843.          " \t\"\{\}\[\]"] " ; # "
  844.     if { $ispellVars(label) == "" } { break }
  845.  
  846.     incr wcount
  847.     if { $wcount > 20 } {
  848.         $window see $startIndex
  849.         set wcount 0
  850.     }
  851.  
  852.     update
  853.     set result [ IspellMarkWord $window $startIndex $stopIndex $word ]
  854.  
  855.     }
  856.  
  857.     # destroy the toplevel window
  858.  
  859.     catch {
  860.     destroy .ispellStop
  861.     }
  862.  
  863.     # put the window back under the insert cursor
  864.  
  865.     $window see insert
  866. }
  867.  
  868.  
  869. # Call this procedure with the text window path to bind the spell command
  870.  
  871. proc Ispellbind { text } { 
  872.  
  873.     global ispellVars;
  874.  
  875.     set command {IspellTextWindow %W} ; 
  876.     
  877.  
  878.     bind $text <Key-space> "IspellTextWindowInsert %W"
  879.     bind $text <Key-Tab> "IspellTextWindowInsert %W" 
  880.     
  881.     bind $text <Key-Right> "$command"   
  882.     bind $text <Key-Left>  "$command"    
  883.     bind $text <Key-Down>  "$command"
  884.     bind $text <Key-Up>    "$command"
  885.     
  886.     bind $text <Key-Return> " $command "
  887.     
  888.     # How do we correct words? Normally button-3!
  889.     bind $text <$ispellVars(popupBinding)> { 
  890.     IspellPostMenuChoices %W %x %y %X %Y 
  891.     } ;
  892.  
  893.     # no user configurable way to select the unpost...
  894.     # tk_popup should unpost the menu for us automatically
  895. #    bind $text <Any-ButtonRelease> { IspellUnPostMenuChoices %W } 
  896.     
  897. }
  898.  
  899.  
  900.     
  901.     
  902.  
  903.  
  904.  
  905.  
  906.  
  907.  
  908.  
  909.